﻿ using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using Galoap.JSON;
using Galoap;
using Galoap.Properties;
using Galoap.UnitTest.Storage;
using Galoap.Questions;
using Galoap.UnitTest;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;

namespace WebService
{
    /// <summary>
    /// Galoap WebService
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [System.Web.Script.Services.ScriptService]
    public class Service1 : System.Web.Services.WebService {

        /// <summary>
        /// Now I reaaaaaaaaaaaaally like it :D
        /// </summary>
        [WebMethod()]
        public List<int> SampleStat(DateTime start, DateTime end)
        {
            return Galoap.Statistics.GetNumberOfActiveUsersFromToDate(null,start, end);
        }

        /// <summary>
        /// JSon representation of the Question
        /// </summary>
        private string JSonQuestion { get; set; }

        /// <summary>
        /// The JSon Encoder
        /// </summary>
        public JSONEncoder JSonEncoder { get; set; }


        /// <summary>
        /// This method is for logging users, it uses JSON strings as params and answers
        /// </summary>
        /// <param name="login">It's a string JSON as {"nickname":"name","password":"pass"}</param>
        /// <returns>It's a string JSON </returns>
        [WebMethod(Description = "Login Method", EnableSession = true)]
        public string Login(string login) {
            WriteToEventLog("Login", EventLogEntryType.Information);
            Hashtable risp = new Hashtable();
            if (checkSession()) {
                //Context.Response.Redirect("sessionTimeout.htm");
                risp["risposta"] = "true";
                risp["logged"] = "true";
                risp["userid"] = ((User)Session["user"]).NickName;
                return JSON.JsonEncode(risp);
            }
            else  {
                try   {
                    Hashtable lista = new Hashtable();
                    //string richiesta = login.ToString();
                    lista = (Hashtable)JSON.JsonDecode(login);
                    // Hashtable lista = (Hashtable)JSON.JsonDecode(login);
           			User user = Galoap.User.Login((string)lista["nickname"],(string)lista["password"]);
                    Session["user"] = user;
                    
					Session.Timeout = 30;
                    risp["risposta"] = "true";
                    risp["userid"] = user.UserID;
                    Session["checkable"] = "false";
                    return JSON.JsonEncode(risp);
                }
                catch (Exception e)   {
                    risp["risposta"] = "false";
                    risp["msgerror"] = e.Message;
                    return JSON.JsonEncode(risp);
                }
            }
        }

        /// <summary>
        /// This method in for register users using also JSON strings as params and answers
        /// </summary>
        /// <param name="registerstring">it's JSON string with the param for registration</param>
        /// <returns></returns>
        [WebMethod(Description = "Register user", EnableSession = true)]
        public string Register(string registerstring)
        {
            WriteToEventLog("Register", EventLogEntryType.Information);
            Hashtable risp = new Hashtable();
            if (checkSession())
            {
                risp["register"] = ((User)Session["user"]).NickName + ": you are already logged in!";
                return JSON.JsonEncode(risp);
            }
            Hashtable lista = (Hashtable)JSON.JsonDecode(registerstring);
            try
            {

                bool exist = true;
                string nick = (string)lista["nickname"];
                try {
                    Galoap.User.GetUserByNickname(nick);
                }
                catch (ArgumentException) { 
                    //utente gia registrato
                    exist = false;
                    
                }
                if (exist) { 
                    return "{\"risposta\":\"false\",\"msg\":\"user\"}";
                }
                exist = true;
                string firstname = (string)lista["firstname"];
                string lastname = (string)lista["lastname"];
                string email = (string)lista["email"];
                try {
                    Galoap.User.GetUserByEmail(email);
                }
                catch (ArgumentException)  {
                    //utente gia registrato
                    exist = false;
                }
                if (exist) { 
                    return "{\"risposta\":\"false\",\"msg\":\"email\"}";                    
                }
                string password = (string)lista["password"];//non usare md5 gia criptata

                User u = new User { Name = firstname, LastName = lastname, NickName = nick, Email = email, Password = password };
     
                //User new_user = new User { Name = "puppa", LastName = "puppa", NickName = "puppa", Email = "puppa@puppa.it", Password = "puppa" };
                Galoap.User.Register(u);

                return "{\"risposta\":\"true\"}";
            }
            catch (Exception ) { return "{\"risposta\":\"false\"}"; }
        }

        /// <summary>
        /// This method is used to give a question for a specific game.
        /// Selects the game from the game name and return a question object.
        /// It also load or create a specific game session for the game
        /// </summary>
        /// <param name="gamename">string, the game name</param>
        /// <returns>a correspondent question object</returns>
        [WebMethod(Description = "Get Question", EnableSession = true)]
        public string GetQuestion(string gamename)
        {
            WriteToEventLog("Getquestion " + gamename, EventLogEntryType.Information);
            //Hashtable lista = new Hashtable();
            Hashtable risp = new Hashtable();
            if (!checkSession())
            {
                //Context.Response.Redirect("sessionTimeout.htm");
                risp["register"] = "You are not logged in!";
                risp["risposta"] = "false";
                return JSON.JsonEncode(risp);
            }
            try
            {

                User player = ((User)Session["user"]);
                Game game = Game.GetGame(gamename);
                GameSession gs = checkGameSession(player, game);
                Question q = null;
                String ret;

                try
                {
                    //if (gamename == "Phratris")
                    //{
                    //    String jprova = "{\"text\":\"posiziona\",";
                    //    jprova += "\"level\":\"1\",";
                    //    jprova += "\"answer\":\"\",";
                    //    jprova += "\"questionid\":\"10\",";
                    //    jprova += "\"idocument\":[\"se piove apro l'ombrelo.\"],";
                    //    jprova += "\"subclass\":{";
                    //    jprova += "\"partofspeech\":[[\"Se\",\"C\"],[\"piove\",\"V\"],[\"apro\",\"V\"],[\"un\",\"R\"],[\"ombrello\",\"S\"],[\".\",\"F\"]],";
                    //    jprova += "\"tree_0\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_1\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_2\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_3\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_4\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_5\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_6\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_7\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_8\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"],";
                    //    jprova += "\"tree_9\":[4,5,\"un ombrello\",2,\"piove un ombrello\",3,\"piove apro\",1,\"Se piove apro un ombrello\",6,\"Se piove apro un ombrello.\"]}}";
                    //    Session["question"] = null;
                    //    Session["checkable"] = "true";
                    //    risp["risposta"] = "true";
                    //    risp["question"] = jprova;
                    //    ret = JSON.JsonEncode(risp);
                    //    return ret;
                    //}
                    if (Session["checkable"] == "true")
                    {
                        risp["risposta"] = "true";
                        risp["question"] = Session["JsonQuestion"];
                        ret = JSON.JsonEncode(risp);
                        return ret;
                    }
                    else{
                        q = this.GetQuestion(gamename, gs);
                    }
                }
                catch (Exception)
                {
                    if (gs.Score != 0)
                    {
                        GameSession.TerminateSession(gs);
                    }
                    risp["risposta"] = "false";
                    risp["gameover"] = "true";
                    risp["terminated"] = gs.Terminated;
                    ret = JSON.JsonEncode(risp);
                    return ret;
                }

                
                Session["question"] = q;
                Session["checkable"] = "true";
                Session["JsonQuestion"] = JSonQuestion;
                risp["risposta"] = "true";
                risp["question"] = JSonQuestion;              

                ret = JSON.JsonEncode(risp);
                return ret;

                //return game.Title;
                //return ((User) Session["user"]).GetLastGameSession().GetQuestion().ToJSON();
            }
            catch (Exception e)
            {
                risp["risposta"] = "false";
                risp["register"] = e.Message + " " + e.GetBaseException();

                return JSON.JsonEncode(risp);
            }

        }

        /// <summary>
        /// This method checks if the answer is correct.
        /// It also controls if it is possible to give an answer to a question
        /// </summary>
        /// <param name="answerstring">a JSON string</param>
        /// <returns>a JSON string</returns>
        [WebMethod(Description = "Check if answer is valid and returns score", EnableSession = true)]
        public string CheckAnswer(string answerstring)
        {
            WriteToEventLog("checkAnswer", EventLogEntryType.Information);
            Hashtable risp = new Hashtable();
            if (checkSession())
            {
                Hashtable lista = (Hashtable)JSON.JsonDecode(answerstring);
                User player = ((User)Session["user"]);
                Game game = Game.GetGame((String)lista["gamename"]);
                // GameSession gs = player.GetLastGameSessionForGame((String)lista["gamename"]);
                GameSession gs = GameSession.GetLastGameSession(player, game);
                Question q = (Question)Session["question"];
                if (q.Validate((String)lista["answer"])&&(Session["checkable"]=="true")) {
                    StringAnswer ans = q.SaveAnswer<StringAnswer>(gs, (String)lista["answer"]);
                    // StringAnswer ans = Answer.CreateAnswer<StringAnswer>(q, gs, (String)lista["answer"]);
                    risp["risposta"] = "true";
                    risp["score"] = (int)gs.Scoring(ans);
                    Session["checkable"] = "false";
                }
                else {
                    risp["risposta"] = "false";
                    risp["gameover"] = "true";
                    risp["terminated"] = gs.Terminated;
                }
                return JSON.JsonEncode(risp);
            }
            else
            {
                risp["risposta"] = "false";
                risp["gameover"] = "true";
                risp["register"] = "You are not logged in!";
                return JSON.JsonEncode(risp);
            }
        }

        /// <summary>
        /// This method is to check the score of an user for a specific game
        /// </summary>
        /// <param name="gamename">sting of the game name</param>
        /// <returns>a JSON string</returns>
        [WebMethod(Description = "Get Score", EnableSession = true)]
        public string GetScore(string gamename)
        {
            WriteToEventLog("getScore", EventLogEntryType.Information);
            //Hashtable lista = new Hashtable();
            Hashtable risp = new Hashtable();
            if (!checkSession())
            {
                //Context.Response.Redirect("sessionTimeout.htm");
                risp["register"] = "You are not logged in!";
                risp["risposta"] = "false";
                return JSON.JsonEncode(risp);
            }
            try
            {
                User player = ((User)Session["user"]);
                Game game = Game.GetGame(gamename);
                //se l'utente è nuovo non ha una gamesession associata al game
                //prevedere l'errore e creare una gamesession
                GameSession gs = checkGameSession(player, game);

                int score = gs.Score;

                risp["risposta"] = "true";
                risp["score"] = ""+score;

                return JSON.JsonEncode(risp);

                //return game.Title;
                //return ((User) Session["user"]).GetLastGameSession().GetQuestion().ToJSON();
            }
            catch (Exception e)
            {
                risp["risposta"] = "false";
                risp["register"] = e.Message;
                return JSON.JsonEncode(risp);
            }

        }

        /// <summary>
        /// This method is used to terminate game sessions
        /// </summary>
        /// <param name="gamename">sting of the game name</param>
        /// <returns>a JSON string</returns>
        [WebMethod(Description = "Terminate", EnableSession = true)]
        public string Terminate(string gamename)
        {
            //Hashtable lista = new Hashtable();
            Hashtable risp = new Hashtable();
            if (!checkSession())
            {
                //Context.Response.Redirect("sessionTimeout.htm");
                risp["register"] = "You are not logged in!";
                risp["risposta"] = "false";
                return JSON.JsonEncode(risp);
            }
            try
            {
                User player = ((User)Session["user"]);
                Game game = Game.GetGame(gamename);

                GameSession gs = GameSession.GetLastGameSession(player, game);

                GameSession.TerminateSession(gs);
               // OnTerminatedChanging(false);
                risp["risposta"] = "true";
                risp["terminated"] = ""+gs.Terminated;

                return JSON.JsonEncode(risp);
            }
            catch (Exception e)
            {
                risp["risposta"] = "false";
                risp["register"] = e.Message;
                return JSON.JsonEncode(risp);
            }

        }

        /// <summary>
        /// Returns an histogram of the active users on a range of days
        /// input d'esempio: {     "start": "1/01/2010",     "end": "7/01/2010"  }
        /// output relativo: {"histogram":"[3, 0, 0, 0, 0, 0, 0]", "risposta":"true"}
        /// </summary>
        /// <param name="intervals"></param>
        /// <returns></returns>
        [WebMethod(Description = "Returns an histogram of the active users on a range of days", EnableSession = false)]
        public string GetUsersPerDayHistogram(string intervals)
        {
            return getStatisticsForFunction(Galoap.Statistics.GetNumberOfActiveUsersFromToDate, intervals);
        }

        [WebMethod(Description = "Returns an histogram of number of games played on a range of days", EnableSession = false)]
        public string GetNumberOfGamesPlayedHistogram(string intervals)
        {
            //TODO: This "json decoder" is the devil, it really should be replaced with a generics compliant one
            //like this one http://json.codeplex.com/ asafp
            return getStatisticsForFunction(Galoap.Statistics.GetNumberOfGamesPlayedFromToDate, intervals);
        }

        [WebMethod(Description = "Returns an histogram of game sessions on a range of days", EnableSession = false)]
        public string GetGameSessionsPlayedHistogram(string intervals)
        {
            //TODO: This "json decoder" is the devil, it really should be replaced with a generics compliant one
            //like this one http://json.codeplex.com/ asafp
            return getStatisticsForFunction(Galoap.Statistics.GetGameSessionsPlayedFromToDate, intervals);
        }

        [WebMethod(Description = "Returns an histogram of games sessions by a specific user on a range of days", EnableSession = false)]
        public string GetNumberOfGameSessionsByUserHistogram(string intervals)
        {
            //TODO: This "json decoder" is the devil, it really should be replaced with a generics compliant one
            //like this one http://json.codeplex.com/ asafp
            return getStatisticsForFunction(Galoap.Statistics.GetNumberOfGameSessionsByUserFromToDate, intervals);
        }

        [WebMethod(Description = "Returns an histogram of the number of answers given by a specific user on a range of days", EnableSession = false)]
        public string GetNumberOfAnswersByUserHistogram(string intervals)
        {
            //TODO: This "json decoder" is the devil, it really should be replaced with a generics compliant one
            //like this one http://json.codeplex.com/ asafp
            return getStatisticsForFunction(Galoap.Statistics.GetNumberOfAnswersByUserFromToDate, intervals);
        }

        private string getStatisticsForFunction(Func<User, DateTime, DateTime, List<int>> method, string intervals)
        {
            Hashtable answer = new Hashtable();
            try
            {
                Hashtable dict = (Hashtable)JSON.JsonDecode(intervals);
                DateTime start = DateTime.Parse((string)dict["start"]);
                DateTime end = DateTime.Parse((string)dict["end"]);
                string username = ((string)dict["user"]);
                User user = null;
                if (username != null)
                    user = Galoap.User.GetUserByNickname(username);
                answer["risposta"] = "true"; //Why are we using Italian names?
                answer["histogram"] = JSON.JsonEncode(new ArrayList(method(user,start, end)));
            }
            catch (Exception e)
            {
                answer["risposta"] = "false"; //WTF, why are we using Italian names?                
                answer["histogram"] = JSON.JsonEncode(new ArrayList());
            }
            return JSON.JsonEncode(answer);
        }


        /// <summary>
        /// This method is used to log out the current user.
        /// It closes the current session.
        /// </summary>
        /// <returns>a simple JSON string</returns>
        [WebMethod(Description = "Logout", EnableSession = true)]
        public string Logout() {
            //Context.Response.Redirect("logout.htm");
            if(checkSession()) 
                Session.Abandon();
            return "{ Logged out }";
        }

        /// <summary>
        /// This method call the specific GetQuestion switching from the gamename. The game developer has to
        /// register his game into this method.
        /// </summary>
        /// <param name="gamename">name of the question</param>
        /// <param name="gs">the gamesession from which we want to extract the question</param>
        /// <returns>a question</returns>
        private Question GetQuestion(string gamename, GameSession gs)
        {
            if (gamename == "Phratris")
            {
                PhratrisQuestion q = gs.GetQuestion<PhratrisQuestion, PhraseDocument>(1, true);
                JSonEncoder = new PhratrisJSONEncoder(q);
                JSonQuestion = JSonEncoder.GetJSON();
                return q;
            }
            else if (gamename == "TagImage")
            {
                ImageTagQuestion q = gs.GetQuestion<ImageTagQuestion, ImageDocument>(1, true);
                JSonEncoder = new ImageTagJSONEncoder(q);
                JSonQuestion = JSonEncoder.GetJSON();
                return q;
            }
            else if (gamename == "PartOfSpeech")
            {
                PartOfSpeechQuestion q = gs.GetQuestion<PartOfSpeechQuestion, PhraseDocument>(1);
                JSonQuestion = null; //deprecated
                return q;
            }

            return null;
        }

        /// <summary>
        /// This method checks if there is a active session
        /// </summary>
        /// <returns>a boolean value</returns>
        private bool checkSession() {
            return Session["user"] != null;
        }

        /// <summary>
        /// This method is used to crypt user passwords using md5 algorithm
        /// </summary>
        /// <param name="input">a string</param>
        /// <returns>a crypted string</returns>
        private string md5(string input) {
            System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(input);
            bs = x.ComputeHash(bs);
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            foreach (byte b in bs) {
                s.Append(b.ToString("x2").ToLower());
            }
            string password = s.ToString();
            return password;
        }

        /// <summary>
        /// This method controls if there is a opened game session for a user in a specific game.
        /// If there is no active game session it creates one.
        /// </summary>
        /// <param name="player">a user object</param>
        /// <param name="game">a game object</param>
        /// <returns>a game session object</returns>
        private GameSession checkGameSession(User player, Game game)
        {
            GameSession gs;
            try
            {
                gs = GameSession.GetLastGameSession(player, game);
            }
            catch (System.InvalidOperationException)
            {
                gs = GameSession.CreateGameSession(player, game);
            }

            if (gs.Terminated)
            {
                gs = GameSession.CreateGameSession(player, game);

            }

            return gs;
        }

        /// <summary>
        /// Method to write to the Event Log
        /// </summary>
        /// <param name="strLogEntry">The message to be logged</param>
        /// <param name="eType">Event Log Type</param>         
        private void WriteToEventLog(string strLogEntry, EventLogEntryType eType)  {
            string strSource = "Division Web Service"; //name of the source
            string strLogType = "Application"; //type of the log
            string strMachine = "."; //machine name

            //if (!EventLog.SourceExists(strSource, strMachine)) {
            //    EventLog.CreateEventSource(strSource, strLogType, strMachine);
            //}
            //EventLog.CreateEventSource(strSource, strLogType, strMachine);
            //EventLog eLog = new EventLog(strLogType, strMachine, strSource);
            //eLog.WriteEntry(strLogEntry, eType, 1000);        
        }
    }

    
}